home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-02-29 | 37.5 KB | 1,344 lines | [TEXT/MPS ] |
- /*******************************************************************\
- * file: atalkXCMD.c *
- * version: 1.06ß *
- * *
- * *
- * The following XCMD's give you AppleTalk capability (at the the *
- * transaction and name-binding protocol layers) to Hypercard. While *
- * not an exhaustive treatment, it's a start. *
- * *
- * code known to work with correctly with the prototype as a client *
- * and/or server *
- * ----------------------------------------------------------------- *
- * Routine | Description *
- * ----------------------------------------------------------------- *
- * *
- * --- AppleTalk Transaction Protocol (ATP) Routines --- *
- * ATPInit Check to see if port available and appletalk drivers*
- * loaded, allocate required buffers. Open a server *
- * socket for this node. *
- * ATPKill Disassociate ourselves with the net deallocate *
- * buffers *
- * PollRequests Poll the Idle queue and return info *
- * Request Determine whether a request can be sent or needs to *
- * be added to the waiting queue. *
- * entity. *
- * *
- * --- Name Binding Protocol (NBP) Routines --- *
- * NBPInit set up the NBP stuff *
- * NBPKill close down the NBP stuff *
- * ConfirmName confirm that an entity is still visible (should be *
- * done before sending a message to that entity) *
- * ExtractName Extract a name from the Lookup buffer *
- * Lookup Look up other entities on the net and put the *
- * results in LookUpBuffer. *
- * NodeRegister Get the name of this node and register it in the *
- * socket listening table. *
- * *
- * --- Miscellaneous Support Routines --- *
- * DeQueueRequest *
- * Remove an element from waiting queue (FIFO style) *
- * QueueGetRequest *
- * Queue a GetRequest on the server side to "listen" *
- * for incoming client requests. *
- * QueueRequest Queue a request that cannot be services due to lack *
- * of sockets. Copy all the data into queue structure *
- * Respond Respond to a request with the given message *
- * SendRequest Set up an ATP parameter block and send the request *
- * SendWaitingRequest *
- * Attempt to send a queued request. If successful, *
- * dequeue it, otherwise leave it in the queue for *
- * another try later. *
- * ----------------------------------------------------------------- *
- * By: Donald Koscheka *
- * with Gratitudes to Kerry Lynn for debugging help *
- * and Platitudes to Greg Kimberly for thinking up the idea *
- * Date: 21-Sept-87 *
- * © Copyright 1987, Apple Computer, Inc. *
- * All Rights Reserved *
- * *
- * ----------------------------------------------------------------- *
- * Modification History *
- * ----------------------------------------------------------------- *
- * Date | By | Description *
- * ----------------------------------------------------------------- *
- * 9/21/87 | DK | file created *
- * 10/1/87 | DK | modified to work with preferred ATP calls *
- * 10/5/87 | DK | relocate the master block at heap high *
- * 10/6/87 | DK | added dynamically allocated lookup buffer *
- * 10/7/87 | DK | separated response from Poll Request *
- * 10/7/87 | DK | extract responses during Poll_Requests *
- * 11/5/87 | DK | modified all routines to return the error *
- * | DK | removed paramBlock from all calls not making *
- * | | callbacks to hypercard. *
- * | | modified code to reflect changes in 11/5 spec *
- * ----------------------------------------------------------------- *
- * Alpha Release 0.5 11/30/87 *
- * ----------------------------------------------------------------- *
- * 12/1/87 | DK | Modified receive to put received data in *
- * | | global pool and call back with method only *
- * 12/2/87 | DK | Modified waiting queue to act as a FIFO *
- * | DK | issue responses asynchronously. *
- * 12/3/87 | DK | Added full response packet transmission *
- * 12/7/87 | DK | Converted NBP calls to preferred (paramblock) *
- * 12/10/87 | DK | General bug fixes - check handles... *
- * 12/11/87 | DK | converted nonrels from locked handles to Ptrs *
- * ----------------------------------------------------------------- *
- * Beta Release 1.05ß 12/15/87 *
- * ----------------------------------------------------------------- *
- * 1/14/88 | DK | Converted all routines to access names table *
- * | | Uncouple NBP and ATP stuff *
- * 1/26/88 | DK | set SPConfigP to 0x01 to indicate Appletalk is*
- * | | is using port B *
- * 2/10/88 | DK | Set atp.checkpoint flag to signal that we're *
- * | | currently in ATPReceive (ATPClose can't close)*
- * | | or ATPClose sets to tell ATPReceive to close *
- * 22-Feb-88 | DK | Move all locked handles high *
- * 29-Feb-88 | DK | Dispose handle created for response callback *
- * ----------------------------------------------------------------- *
- \*******************************************************************/
-
- /*******************************************************************\
- * HUH???? Experienced "C" programmers will note the capricious *
- * use of the commented "auto" storage class. I do this so that *
- * my data declarations "line up" as: CLASS TYPE NAME = INITIALIZER *
- * This pedantry is provided as an aid to the beginner. Also, *
- * remembering that automatics are stack-bound might help the novice *
- * look for them in a stack frame from the debugger(Hint: A6 is the *
- * frame pointer). *
- * -DK *
- \*******************************************************************/
-
-
- #include <Types.h>
- #include <AppleTalk.h>
- #include <Resources.h>
- #include <OSUtils.h>
- #include <Memory.h>
- #include <Strings.h>
- #include "atalkxcmd.h"
- #include "xcmdutils.h"
- #include "HyperXCmd.h"
-
-
- /*******************************************************************\
- * AppleTalk Transaction Protocol (ATP) Interface Routines *
- * *
- \*******************************************************************/
-
- ATPBlock *ATPInit( entcount, type )
- short entcount;
- short type;
- /*********************************
- * Check to see if PortB is in use by
- * a serial driver or wheter it is
- * available to us for appletalk. If
- * it is available, check PortBUse to
- * determine whether appletalk is in.
- *
- * Allocate our buffers and open a
- * listening socket for this node
- * (talkers are allocated dynamically).
- * queue up enough requests to get
- * us going...
- *
- * In: short = number of entitities
- * to allocate
- *
- * Out: Pointer to the allocated ATPBlock
- * nil of the session was already initialized
- *
- * NOTE: We attempt to allocate (3)
- * nonrels - ATPBlock, ServerBlock
- * and ClientBlock. These objects
- * MUST NOT move during the session
- * (async I/O is depending on it)
- *********************************/
- {
- /* auto */ char *PortBUseP;
- /* auto */ char *SPConfigP;
- /* auto */ char use, con;
- /* auto */ char *temp;
- /* auto */ OSErr error;
- /* auto */ short i;
- /* auto */ ATPParamBlock pb;
- register ATPBlock *mbPtr;
-
- /* point to and get the needed lowmem stuff. */
- PortBUseP = (char *)PortBUse;
- SPConfigP = (char *)SPConfig;
- use = *PortBUseP;
- con = *SPConfigP;
-
- if((con & 0x0F) > useATalk) /** if the port is in use we can't usurp it **/
- return( nil );
-
- if( (use & 0xFF || use & 0x0F) == useATalk){
- if(!IsMPPOpen())
- if((error = MPPOpen()) != noErr)
- return( nil );
-
- if( !IsATPOpen() ){
- if (error = ATPLoad() != noErr)
- return( nil );
-
- *PortBUseP |= 0x04;
- *PortBUseP &= 0x7F;
- }
- }
- else
- return( nil );
-
- *SPConfigP |= 0x01; /*** set for appletalk ***/
-
- /*** attempt to allocate the data ***/
- if( !(mbPtr = (Ptr)NewPtr( sizeof( ATPBlock ))) )
- return( nil );
-
- /*** ------ Initialize the Master Block ------ ***/
- mbPtr->Server = nil;
- mbPtr->Client = nil;
- mbPtr->FirstRequest = nil; /*** no requests queued ***/
- mbPtr->LastRequest = nil;
- mbPtr->checkPoint = CLOSE_OK; /**** 2/10/88 ***/
-
- temp = mbPtr->autoRsp;
- *temp++ = 'O'; /*** if you receive this message ***/
- *temp++ = 'K'; /*** from a callback, you know the ***/
- *temp++ = 'A'; /*** transaction succeeded. ***/
- *temp++ = 'Y'; /*** Isn't that special? ***/
- *temp = '\0';
-
- /*** ------ Initialize the server side ------ ***/
- mbPtr->ServerAddr.aSocket = 0;
- mbPtr->ServerAddr.aNode = 0;
- mbPtr->ServerAddr.aNet = 0;
-
- if( type && SERVER ){
- pb.ATPaddrBlock.aNet = (short)0;
- pb.ATPaddrBlock.aNode = 0;
- pb.ATPaddrBlock.aSocket = 0;
- pb.ATPatpSocket = 0;
-
- if( !POpenATPSkt( &pb , SYNC ) ) /*** noErr means we have a socket***/
- mbPtr->ServerAddr.aSocket = (unsigned char)pb.ATPatpSocket;
- else{
- ShutMeDown( mbPtr );
- return( nil );
- }
-
- /*** initialize getrequests array ***/
- {
- register ServerPtr sp = mbPtr->Server;
-
- mbPtr->Server = (ServerPtr)NewPtr( sizeof( ServerBlock )*SQSIZE );
- if( mbPtr->Server )
- sp = mbPtr->Server;
- else{
- ShutMeDown( mbPtr );
- return( nil );
- }
- for( i=0; i < SQSIZE; i++ )
- QueueGetRequest( &(sp[i]), &(mbPtr->ServerAddr) );
- }
- }
-
- /*** ------ Initialize the client side ------ ***/
- if( type && CLIENT ){
- /*** initialize the send request queue ***/
- {
- ClientPtr cp;
-
- mbPtr->Client = (ClientPtr)NewPtr( sizeof( ClientBlock ) * CQSIZE );
- if( mbPtr->Client )
- cp = mbPtr->Client;
- else{
- ShutMeDown( mbPtr );
- return( nil );
- }
- for( i=0; i < CQSIZE; i++ ){
- cp[i].isInUse = IDLE;
- cp[i].rspM = nil;
- }
- }
- }
- return( mbPtr );
- }
-
-
- ShutMeDown( atp )
- ATPBlock *atp;
- /****************************
- * Given a handle to an allocated
- * ATPBlock, deallocate all its
- * substructures and itself
- *
- * called only after a memory failure
- * and atp already allocated.
- ****************************/
- {
- if( atp->Server )
- DisposPtr( atp->Server );
-
- if( atp->Client )
- DisposPtr( atp->Client );
-
- DisposPtr( atp );
- }
-
-
- short ATPKill( atp )
- ATPBlock *atp;
- /*********************************
- * Remove ourselves from the entity
- * table, dislocate our socket and
- * deallocate all memory whose handles
- * were provided...
- *
- * In: Handle to ATPBlock
- *
- * Out: noErr if successful
- * OSErr otherwise
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ OSErr err;
- /* auto */ short i;
- /* auto */ ATPParamBlock pb;
-
- /*** Close Down and Deallocate the SERVER side ***/
- if( atp->Server ){
- register ServerPtr sp = atp->Server;
-
- if( atp->ServerAddr.aSocket ){
- /*** close down any outstanding get requests***/
-
- for( i = 0; i < SQSIZE; i++ )
- if( sp[i].isInUse && sp[i].pb.ATPioResult > 0 ){
- pb.ATPaKillQEl = &(sp[i].pb);
- err = PKillGetReq( &pb, SYNC );
- if( err )
- error = err;
- }
-
- /*** shut down the server socket ***/
- pb.ATPatpSocket = atp->ServerAddr.aSocket;
- err = PCloseATPSkt( &pb, SYNC );
- if( err )
- error = err;
- }
- }
-
- /*** Close Down and Deallocate CLIENT side ***/
- if( atp->Client ){
- register ClientPtr cp = atp->Client;
-
- /*** Kill outstanding SendRequests ***/
- for( i = 0; i < CQSIZE; i++ )
- if( cp[i].isInUse && cp[i].pb.ATPioResult > 0 ){
- pb.ATPaKillQEl = &(cp[i].pb);
- err = PKillSendReq( &pb, SYNC );
- if( err )
- error = err;
-
- if( cp[i].rspM ){
- HUnlock( cp[i].rspM );
- DisposHandle( cp[i].rspM );
- }
- }
-
- /*** deallocate all waiting queue elements ***/
- if( atp->FirstRequest ){
- /* auto */ ClientQH nq, cq;
- register ClientQPtr pq;
-
- cq = atp->FirstRequest;
-
- while( cq ){
- nq = (**cq).next;
- pq = *cq;
-
- if( pq->rsp ){
- HUnlock( pq->rsp );
- DisposHandle( pq->rsp );
- }
-
- if( pq->buf ){
- HUnlock( pq->buf );
- DisposHandle( pq->buf );
- }
- HUnlock( cq );
- DisposHandle( cq );
- cq = nq;
- }
- }
- }
- ShutMeDown( atp );
- return( error );
- }
-
-
- short PollRequests( paramPtr, atp, rspMess )
- XCmdBlockPtr paramPtr;
- ATPBlock *atp;
- Handle rspMess;
- /*********************************
- * PollRequests
- *
- * Performs 3 functions:
- *
- * (1) Handle Received Requests on Server Side
- *
- * (2) Handle Succesful termination of Responses
- *
- * (3) Handle Received Responses on Client Side
- *
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ short i;
-
- if( atp->Server ){
- error = Get_Requests_From_Clients( paramPtr, atp, rspMess );
- error = Check_Response_Sent( paramPtr, atp );
- }
-
- if( atp->Client )
- error = Get_Responses_From_Servers( paramPtr, atp );
-
- if( error > 0 )
- error = 0;
- return( error );
- }
-
-
- short Request( atp, theData, theSize, addr, rmess, cnt, intv )
- ATPBlock *atp;
- unsigned char *theData;
- short theSize;
- AddrBlock *addr;
- Handle rmess;
- short cnt, intv;
- /*********************************
- * If a transaction block is available,
- * send the request on to ATP. Otherwise,
- * add it to the waiting queue.
- *
- * In: theData == the data to use
- * theSize == the size of the data
- * rmess == handle to the message to
- * append the response to
- * addr == tuple of the receiver
- * retry, timeout
- *
- * Out: noErr if successful
- * OSErr otherwise
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ short i;
- register ClientPtr cp = atp->Client;
-
- if( !atp->Client ) /*** take a powder you chowder head ***/
- return( DEFAULT_ERROR );
-
- /*** find the next available client transaction block (if any) ***/
- for( i = 0; i < CQSIZE; i++ )
- if( cp[i].isInUse == IDLE ){
- error = SendRequest( &(cp[i]),theData, theSize, rmess, addr, cnt, intv );
- return( error );
- }
-
- /*** didn't have an available transaction block, or the request failed to ***/
- /*** to get satisfied (probably no available sockets), so pend the request ***/
- QueueRequest( atp, theData, theSize, rmess, addr, cnt, intv );
-
- return( noErr ); /*** queued requests don't fail ***/
- }
-
-
- /*******************************************************************\
- * PollRequests Server/Client Routines *
- \*******************************************************************/
-
-
- Get_Requests_From_Clients( paramPtr, atp, rspMess )
- XCmdBlockPtr paramPtr;
- ATPBlock *atp;
- char **rspMess;
- /*********************************
- * Get_Requests_From_Clients
- *
- * For all elements in the getRequest queue:
- * if a getRequest is completed:
- * - get the request data and put it into the global receive container
- * - call hypercard back with the getrequest message
- * - the responding method will put the response in "GlobalRSPData"
- * - if the response message is nil, do an auto respond
- * else send the response in the global container "GlobalRSPData"
- * - deallocate the request and requeue a fresh one
- *
- * In: paramPtr = used for callbacks
- * atp = ATPBlock pointer
- * rspMess = message to send back to
- * hypercard with Server-side getrequests.
- *
- * Out: calls back hypercard with the request
- * error result returned.
- *********************************/
- {
- /* auto */ short i;
- /* auto */ OSErr error = noErr;
- /* auto */ long rs;
- /* auto */ Handle name, respData;
- /* auto */ long respSize;
- register ServerPtr sp;
-
- if( rspMess )
- c2pstr( *rspMess );
-
- sp = atp->Server;
- for( i = 0; i < SQSIZE ; i++ ){
- error = sp[i].pb.ATPioResult; /*** didn't take***/
- if( sp[i].isInUse == WAIT_REQUEST ){
- if( error < 0 )
- QueueGetRequest( &(sp[i]), &(atp->ServerAddr) );/*** try again ***/
- else
- if( !error ){
- /*** put the request in a Hypercard global ***/
- name = GetResource('STR ', GlobalRcvData );
- respSize = (long)sp[i].pb.ATPreqLength;
- respData = NewHandle( respSize );
- BlockMove( sp[i].pb.ATPreqPointer, *respData, respSize );
- MoveHHi( name );
- HLock(name);
- SetGlobal( paramPtr, *name, respData );
- HUnlock(name);
- DisposHandle( respData ); /* 2/29/88 */
-
- /*** now call the responding method (if any) ***/
- if( rspMess ){
- MoveHHi( name );
- HLock( rspMess );
- SendCardMessage( paramPtr, *rspMess );
- HUnlock( rspMess );
- }
-
- /*** if the responding method has a response to send ***/
- /*** then send it, otherwise "autorespond" using the ***/
- /*** canned response "OKAY" ***/
- name = GetResource('STR ',GlobalRspData );
- MoveHHi( name );
- HLock(name);
- respData = GetGlobal(paramPtr,*name);
- HUnlock(name);
-
- if ( respData && **respData ){
- rs = GetHandleSize( respData );
- MoveHHi( respData );
- HLock( respData );
- Respond( &(sp[i]), &(atp->ServerAddr), *respData, (short)rs );
- HUnlock( respData );
- }
- else /*** send the autoresponse ***/
- Respond( &(sp[i]),&(atp->ServerAddr),atp->autoRsp,(short)AUTOSIZE);
-
- if( respData )
- DisposHandle( respData ); /* 2/29/88 */
- }
- }
- }/*** for loop ***/
- }
-
-
- Check_Response_Sent(paramPtr, atp )
- XCmdBlockPtr paramPtr;
- ATPBlock *atp;
- /*********************************
- * Send_Responses_To_Clients
- *
- * A server transaction block is considered
- * in use from the time that the get request
- * is queued unitl the TREL is received from
- * the client (acknowledging receipt of the
- * response).
- *
- * Actually, all this routine does is wait
- * for the sent response to complete and
- * requeue a get request when it does.
- *
- * In: paramPtr = used for callbacks
- * atp = ATPBlock pointer
- *
- *********************************/
- {
- /* auto */ short i;
- /* auto */ OSErr error = noErr;
- register ServerPtr sp;
-
- /*** See if any Responses have completed ***/
- sp = atp->Server;
- for( i = 0; i < SQSIZE; i++ )
- if( sp[i].isInUse == WAIT_RESPONSE )
- /*** waiting for the client to acknowledge receipt ***/
- if( sp[i].pb.ATPioResult <= 0 ){
- error = sp[i].pb.ATPioResult;
- sp[i].isInUse == IDLE;
- QueueGetRequest( &(sp[i]), &(atp->ServerAddr) );
- }
- }
-
-
- Get_Responses_From_Servers(paramPtr, atp )
- XCmdBlockPtr paramPtr;
- ATPBlock *atp;
- /*********************************
- * Get_Responses_From_Servers
- *
- * For all elements in the request queue:
- * if a sndRequest completes:
- * - get the responding data and put it into the global receive containter
- * - if the request contained a response message, call hypercard back
- * with the message.
- * - dequeue this request
- *
- * In: paramPtr = used for callbacks
- * atp = ATPBlock pointer
- *
- * Out: calls back hypercard with the request
- * error result returned.
- *********************************/
- {
- /* auto */ short i,j;
- /* auto */ OSErr error = noErr;
- /* auto */ ClientPtr cp;
- /* auto */ Handle name, RcvData;
- register char *respData;
- register short respSize;
-
- cp = atp->Client;
- for( i = 0; i < CQSIZE; i++ )
- if( cp[i].isInUse == IDLE )
- SendWaitingRequest( atp, &(cp[i]) );
- else{
- if( cp[i].pb.ATPioResult < 0 ){
- /*** next user of the block will clean up the dangling data ***/
- cp[i].isInUse = IDLE;
- error = cp[i].pb.ATPioResult;
- if( cp->rspM ){
- HUnlock( cp->rspM );
- DisposHandle( cp->rspM );
- cp->rspM = nil;
- }
- }
- if( cp[i].pb.ATPioResult == 0 ){
- /*** got an answer from the server... ***/
- /*** ***/
- /*** To calculate the size, add up the sizes of each BDS ***/
- /*** element. The buffers are contiguous so we don't have ***/
- /*** to concatentate them to create the response message ***/
- cp[i].isInUse = IDLE;
- respData = (cp[i]).bds[0].buffPtr;
- respSize = 0;
-
- for( j = 0; j < (short)cp[i].pb.ATPnumOfResps; j++ )
- respSize += cp[i].bds[j].dataSize;
-
- /*** put the responding data in global pool and call back ***/
- /*** hypercard with the response message ***/
- name = (StringPtr *)GetResource('STR ', GlobalRcvData );
- RcvData = NewHandle( (long)respSize );
-
- if( RcvData ){
- BlockMove( respData, *RcvData, (long)respSize );
- MoveHHi( name );
- HLock(name);
- SetGlobal(paramPtr, *name, RcvData );
- HUnlock(name);
- DisposHandle( RcvData ); /* 2/29/88 */
- }
-
- /*** If response message was given, call back hypercard with ***/
- /*** the message (handler finds its data in GlobalRcvData) ***/
- if( cp[i].rspM ){
- MoveHHi( cp[i].rspM );
- HLock( cp[i].rspM );
- c2pstr( *(cp[i].rspM) );
- SendCardMessage( paramPtr, *(cp[i].rspM) );
- HUnlock( cp[i].rspM );
- DisposHandle( cp[i].rspM );
- cp[i].rspM = nil;
- }
- }
- }
- return( error );
- }
-
-
- /*******************************************************************\
- * (ATP) Interface Support Routines *
- * *
- * These routines are used in support of the ATP Interface routines *
- * and are not called directly by the XCMD routines. *
- \*******************************************************************/
-
-
- DeQueueRequest( atp )
- ATPBlock *atp;
- /*********************************
- * Remove an element from the waiting
- * queue and update the linked list
- * accordingly.
- *
- * Note: this is the only place an element can
- * be removed from the queue. We use a FIFO
- * method so that tq is ALWAYS pointing to
- * the first element in the list!
- *
- * In: tq = handle to the element
- * to dequeue
- *********************************/
- {
- register ClientQH tq = atp->FirstRequest;
- register ClientQPtr pq; /*** pointer for efficiency ***/
-
- if( !tq ){
- atp->FirstRequest = atp->LastRequest = nil;
- return; /*** No queue element here ***/
- }
-
- pq = *tq;
- /*** next becomes first ***/
- if( pq->next ) /*** if there is one ***/
- atp->FirstRequest = pq->next;
- else /*** ...else the list is empty ***/
- atp->FirstRequest = atp->LastRequest = nil;
-
- if( pq->buf ){
- HUnlock( pq->buf ); /*** Delete the element's data ***/
- DisposHandle( pq->buf );
- }
-
- if( pq->rsp ){
- HUnlock( pq->rsp );
- DisposHandle( pq->rsp );
- }
-
- HUnlock( tq );
- DisposHandle( tq );
- }
-
-
- QueueGetRequest( sp, srcaddr )
- ServerPtr sp;
- AddrBlock *srcaddr;
- /*********************************
- * requeue the get request element
- * whose index is passed as a param.
- *
- * Note: This is a server side
- * function only. You may
- * have more than one getrequest
- * "listening" on the server socket.
- *
- * if the request if not completed,
- * do nothing.
- *
- * In: sp == pointer to request array element
- * srcaddr == source address of this node
- *
- * returns nil if the request was queued
- * noErr otherwise
- *********************************/
- {
- /*auto */ short error = DEFAULT_ERROR;
- register atpPBptr pb = &(sp->pb);
-
- pb->ATPioCompletion = nil;
- pb->ATPatpSocket = srcaddr->aSocket;
- pb->ATPreqLength = (short)ATPBSIZE;
- pb->ATPreqPointer = sp->buf;
- sp->isInUse = WAIT_REQUEST; /*** set for the sake of Kill ***/
-
- if( (error = PGetRequest( pb , ASYNC )) >= 0 )
- error = noErr;
-
- return( error );
- }
-
-
- QueueRequest( atp, theData, theSize, rmess, addr, cnt, intv )
- ATPBlock *atp;
- unsigned char *theData;
- short theSize;
- Handle rmess;
- AddrBlock *addr;
- short cnt, intv;
- /*********************************
- * Add an element to the waiting
- * queue and update the linked list
- * accordingly.
- *
- * Note: this is the only place an element can
- * be added to the queue.
- *
- * In: theData == the data to use
- * theSize == the size of the data
- * rmess == handle to the message to
- * append the response to
- * addr == tuple of the receiver
- * retry, timeout
- *********************************/
- {
- /* auto */ long respSize;
- /* auto */ ClientQH qelem;
- register ClientQPtr qp; /*** ptr to client queue element***/
-
- if( (qelem = NewHandle( sizeof( ClientQRec ))) == nil )
- return( DEFAULT_ERROR );
-
- /*** copy the pending data into the qelement ***/
- MoveHHi( qelem );
- HLock( qelem );
- qp = *qelem;
- qp->next = nil;
- qp->interval = intv;
- qp->count = cnt;
- qp->addr.aNet = addr->aNet;
- qp->addr.aNode = addr->aNode;
- qp->addr.aSocket= addr->aSocket;
- qp->size = (long)theSize;
- qp->rsp = nil;
- qp->buf = NewHandle( qp->size );
-
- if( qp->buf )
- BlockMove( theData, *(qp->buf), qp->size );
- else{
- HUnlock( qelem );
- DisposHandle( qelem );
- return( MEM_ERROR );
- }
-
- if( rmess ){ /*** if we have a handle and it's not empty ***/
- respSize = GetHandleSize( rmess );
- qp->rsp = NewHandle( respSize );
- if( qp->rsp )
- BlockMove( *rmess, *(qp->rsp), respSize );
- else{
- HUnlock( qp->buf );
- DisposHandle( qp->buf );
- HUnlock( qelem );
- DisposHandle( qelem );
- return( MEM_ERROR );
- }
- }
-
- /*** Only after we're sure we put the request together do we add it to ***/
- /*** the queue. ***/
- if( !atp->FirstRequest )
- atp->FirstRequest = atp->LastRequest = qelem;
- else{ /*** add to end of list ***/
- qp = *(atp->LastRequest);
- qp->next = qelem;
- atp->LastRequest = qelem;
- }
- HUnlock( qelem );
- }
-
-
-
- short Respond( sp, srcAddr, theData, theSize )
- ServerPtr sp;
- AddrBlock *srcAddr;
- char *theData;
- short theSize;
- /*********************************
- * Send a response to a requesting
- * socket. This is a server side
- * function only.
- *
- * This routine reuses the parameter
- * block that was used by the getrequest.
- * Because of this, the destination address
- * and the transaction ID will already be
- * valid.
- *
- * In: sp == pointer to the server block to use
- * srcAddr == this server's address
- * theData == the data to use
- * theSize == the size of the data
- *
- * Out: noErr if successful
- * OSErr otherwise
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ short bdsSize, i, bdsCount, bdsBufSiz;
- register char *bdsPtr;
- register atpPBptr pb = &(sp->pb);
-
- /*** Note: because we are re-using the getrequest parameter block, the ***/
- /*** transaction ID and the destination address are already set in the ***/
- /*** parameter block. ***/
- sp->isInUse = WAIT_RESPONSE;
- pb->ATPioCompletion = nil;
- pb->ATPatpSocket = srcAddr->aSocket;
- pb->ATPatpFlags = atpEOMvalue;
- pb->ATPbdsPointer = sp->rspBDS;
-
- /*** set up the response BDS for this transactioncopy the data into the ***/
- /*** rspbuffer (set last byte to zero ) ***/
-
- bdsSize = ( theSize < (ATPBSIZE * MAXBDS) )? theSize : (ATPBSIZE * MAXBDS);
- BlockMove( theData, sp->rspBuf, (long)bdsSize );
- (sp->rspBuf)[bdsSize] = '\0'; /*** force to a "C" string ***/
-
- /*** Set up response bds by pointing into the data at ATPBSIZE intervals ***/
-
- bdsPtr = sp->rspBuf;
- bdsCount = ( bdsSize + ATPBSIZE ) / (short)ATPBSIZE; /*** # bds elements ***/
-
- for( i = 0; i < bdsCount; i++ ){
- sp->rspBDS[i].buffPtr = bdsPtr;
- bdsBufSiz = ( bdsSize < ATPBSIZE)? bdsSize : ATPBSIZE;
- sp->rspBDS[i].buffSize = bdsBufSiz;
- bdsSize -= ATPBSIZE;
- if( bdsSize < 0 )
- bdsSize = 0;
- bdsPtr += ATPBSIZE;
-
- /*** fix up the BDS to make it pretty for the scope ***/
- sp->rspBDS[i].userBytes = (long)'BDS0';
- sp->rspBDS[i].userBytes += (long)i;
-
- }
-
- pb->ATPbdsSize = (Byte)bdsCount; /*** total # of bds elems ***/
- pb->ATPnumOfBuffs = (Byte)bdsCount; /*** number being sent now ***/
-
- PSendResponse( pb, ASYNC ); /*** will catch the error on idle ***/
- return( error );
- }
-
-
- SendRequest( cp, theData, theSize, rmess, addr, count, interval )
- ClientPtr cp;
- unsigned char *theData;
- short theSize;
- Handle rmess;
- AddrBlock *addr;
- short count, interval;
- /*********************************
- * Sends the request using the transaction
- * block pointed to by cp. This is a client
- * side function only.
- *
- * In: cp == pointer to transaction block
- * theData == the data to use
- * theSize == the size of the data
- * rmess == the method to send back with the response
- * addr == tuple of the receiver
- * retry, timeout
- *
- * Out: noErr if successful
- * OSErr otherwise
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ long rsize;
- /* auto */ short i;
- /* auto */ char *bdsPtr;
- register atpPBptr pb = &(cp->pb);
-
- cp->rspM = nil;
-
- /*** copy the message over ***/
- if( rmess ){
- rsize = GetHandleSize( rmess );
- cp->rspM = NewHandle( rsize );
- if( !cp->rspM )
- return( DEFAULT_ERROR );
- else
- BlockMove( *rmess, *(cp->rspM), rsize );
- }
-
- /*** set up the parameter block ***/
- pb->ATPioCompletion = nil;
- pb->ATPuserData = (long)0x52455153; /*** REQS ***/
- pb->ATPatpFlags = atpXOvalue;
- pb->ATPaddrBlock.aNet = addr->aNet;
- pb->ATPaddrBlock.aNode = addr->aNode;
- pb->ATPaddrBlock.aSocket= addr->aSocket;
-
- /*** Set up the request BDS ***/
- /*** first copy the data into the local buffer ***/
- theSize = (theSize < ATPBSIZE )? theSize : ATPBSIZE;
- BlockMove( theData, cp->buf, (long)theSize );
- (cp->buf)[theSize] = '\0'; /*** request MUST BE null terminated ***/
- pb->ATPreqLength = theSize;
- pb->ATPreqPointer = cp->buf;
-
- /*** set up the response BDS ***/
- bdsPtr = cp->rsp;
-
- for( i = 0; i < MAXBDS; i++ ){
- cp->bds[i].buffPtr = bdsPtr;
- cp->bds[i].buffSize = ATPBSIZE;
- cp->bds[i].dataSize = 0;
- cp->bds[i].userBytes = 0L;
- bdsPtr += ATPBSIZE;
- }
-
- pb->ATPbdsPointer = cp->bds; /*** bds points to response ***/
- pb->ATPnumOfBuffs = MAXBDS; /*** max num of response elements ***/
-
- /*** Do the timeout parameters and issue the request... ***/
- pb->ATPtimeOutVal = interval;
- pb->ATPretryCount = count;
- cp->isInUse = WAIT_RESPONSE;
-
- error = PSendRequest( pb, ASYNC); /*** error will be picked up later ***/
-
- if( error >= 0 )
- error = noErr;
- else /*** we fell down, so deallocate ***/
- if( cp->rspM ){
- HUnlock( cp->rspM );
- DisposHandle( cp->rspM );
- cp->rspM = nil;
- }
- return( error );
- }
-
-
- SendWaitingRequest( atp, cp )
- ATPBlock *atp;
- ClientPtr cp;
- /*********************************
- * Attempt to send a waiting request from
- * the waiting queue using the transaction
- * block whose index is passed.
- *
- * We step through the waiting queue until
- * we find the next available element. Attempt
- * to send it. If it sends ok, delete it from
- * the queue, otherwise, keep it around for a
- * whack at it later.
- *
- * In: i == index to available client
- * transaction block
- *
- *********************************/
- {
- /* auto */ short error = noErr;
- register ClientQH rq = atp->FirstRequest;
- register ClientQPtr p = nil;
-
- if( !rq )
- return( NO_ERROR ); /*** nobody in the queue ***/
-
- MoveHHi( rq );
- HLock( rq );
- p = *rq;
-
- if( p->buf ){ /*** only send if we have data ***/
- MoveHHi( p->buf );
- HLock( p->buf );
- error = SendRequest(cp,*(p->buf),(short)p->size,p->rsp,&(p->addr),p->count,p->interval);
- HUnlock( p->buf );
- }
- HUnlock( rq );
- if( !error )
- DeQueueRequest( atp ); /*** got through, take the request away ***/
-
- }
-
-
-
-
- /*******************************************************************\
- * Name Binding Protocol (NBP) Routines *
- \*******************************************************************/
-
- NBPBlock *NBPInit()
- /*********************************
- * Initialize the NBP side for the
- * network.
- *
- * Returns a pointer to an NBP block
- * if the open was successful, nil
- * otherwise.
- *
- *********************************/
- {
- char *temp;
- short i;
- NBPBlock *nbp = nil;
-
- /*** attempt to allocate the data ***/
- if( !(nbp = (Ptr)NewPtr( sizeof( NBPBlock ))) )
- return( nil );
-
- /*** ------ Initialize the NBP side ------ ***/
- nbp->NTEntry.nt.nteAddress.aSocket = 0;
- nbp->NTEntry.nt.nteAddress.aNode = 0;
- nbp->NTEntry.nt.nteAddress.aNet = 0;
-
- temp = nbp->NTEntry.nt.entityData;
- for( i = 0; i < 99; i++ ) /*** zero out the name string ***/
- *temp++ = '\0';
-
- nbp->LookUpBuffer = nil;
- nbp->EntCount = 0;
- nbp->Registered = false;
-
- return( nbp );
- }
-
-
- short NBPKill( nbp )
- NBPBlock *nbp;
- /*********************************
- * Disassociate this node with the network
- * and release all memory used by NBP
- *
- * No nbp calls are made asynchronously
- * so no need to shut any of them down.
- *********************************/
- {
- /* auto */ OSErr error = noErr;
- /* auto */ char *temp;
- /* auto */ MPPParamBlock pb;
-
- /*** Close Down and deallocate NBP side ***/
- temp = nbp->NTEntry.nt.entityData;
- if( *temp ){
- pb.NBPentityPtr = nbp->NTEntry.nt.entityData;
- error = PRemoveName( &pb, SYNC );
- }
-
- if( nbp->LookUpBuffer ){
- HUnlock( nbp->LookUpBuffer );
- DisposHandle( nbp->LookUpBuffer );
- }
-
- DisposPtr( nbp );
- return( error );
- }
-
-
- short ConfirmName( nbp, name, addr, count, interval )
- NBPBlock *nbp;
- EntityName *name;
- AddrBlock *addr;
- short count, interval;
- /*********************************
- * Confirm that the name of a network
- * visible entity is still visible
- * to us.
- *
- * In: name = pointer to name
- * in the form of a names table entry.
- * addr = pointer to address
- *
- * Out: true if successful, socket set in addr
- * false otherwise
- *********************************/
- {
- /*auto */ OSErr error = noErr;
- register MPPParamBlock Mpb;
-
- Mpb.MPPioCompletion = nil; /*** as a matter of form ***/
-
- Mpb.NBPentityPtr = name;
- Mpb.NBPconfirmAddr.aNet = addr->aNet;
- Mpb.NBPconfirmAddr.aNode = addr->aNode;
- Mpb.NBPconfirmAddr.aSocket = addr->aSocket;
- Mpb.NBPnewSocket = 0;
- Mpb.NBPcount = count;
- Mpb.NBPinterval = interval;
-
- if( (error = PConfirmName( &Mpb, SYNC )) == noErr )
- addr->aSocket = Mpb.NBPmaxToGet;
-
- if( error )
- return( false );
- else
- return( true );
- }
-
-
-
- short ExtractName( nbp, who, name, addr )
- NBPBlock *nbp;
- short who;
- EntityName *name;
- AddrBlock *addr;
- /*********************************
- * Extract a name from the socket
- * listening table by index. Sets the
- * send entity to this entity.
- *
- * In: who == Index to name to extract
- * name == pointer to name result
- * addr == pointer to addr result
- * Out: 1 if successful
- * 0 if not successful
- * -1 if all entities searched
- *
- * Note that there is no "preferred" call
- * for extract name. Because NBPExtract
- * doesn't need a parameter block, no preferred
- * call was needed.
- *********************************/
- {
- /* auto */ short error;
- /* auto */ short found = 0;
-
- if( (nbp->EntCount <= 0) || (who > nbp->EntCount) )
- return( -1 ); /*** no network visible entities ***/
-
- MoveHHi( nbp->LookUpBuffer );
- HLock( nbp->LookUpBuffer );
- if( !(NBPExtract( *(nbp->LookUpBuffer),nbp->EntCount,who,name,addr)) )
- found = 1;
-
- HUnlock( nbp->LookUpBuffer );
-
- return( found );
- }
-
-
- short Lookup( nbp, theType, theZone, num, count, interval )
- NBPBlock *nbp;
- char *theType;
- char *theZone;
- short num, count, interval;
- /*********************************
- * Lookup all network visible entities
- * and fill the Lookup buffer accordingly
- *
- * In: (Handle) NBPBlock
- * theType = type to lookup
- * theZone = zone to lookup
- * num = number to lookup
- * count = (number of retries)
- * interval= (X8 ticks)time between retries
- *
- * Out: number of elements returned
- *********************************/
- {
- /* auto */ short error;
- /* auto */ short i;
- /* auto */ NamesTableEntry nte; /*** used internally by PLookupName ***/
- /* auto */ long EntSize;
- /* auto */ MPPParamBlock Mpb;
- register char *np; /*** to fill out the names table ***/
-
- /*** resize the record ***/
- if( nbp->LookUpBuffer ){
- HUnlock( nbp->LookUpBuffer );
- DisposHandle( nbp->LookUpBuffer );
- }
-
- nbp->EntCount = 0;
- EntSize = ( sizeof( NamesTableEntry) );
- nbp->LookUpBuffer = NewHandle( (long)(num * EntSize) );
-
- if( nbp->LookUpBuffer ){
- MoveHHi( nbp->LookUpBuffer );
- HLock( nbp->LookUpBuffer );
- }
- else
- return( DEFAULT_ERROR ); /*** no room left for lookup buffer ***/
-
- /*** Set the Search string to lookup everyone of requested type ***/
- /*** the search string gets put away in a names table entry for ***/
- /*** the preferred AppleTalk call ***/
- /*** ----------------------------------------------------------- ***/
- /*** The use of a names table entry as opposed to an EntityName is ***/
- /*** a matter of form. Lookup wants to see the name in tuple form ***/
- /*** (i.e. the entityname is packed). We borrow the entityData from ***/
- /*** the namestableentry element (nte) and ignore the rest of the ***/
- /*** structure. The use of a string[99] would have the same effect. ***/
-
- nte.qNext = nil;
- nte.nt.nteAddress.aNet = 0;
- nte.nt.nteAddress.aNode = 0;
- nte.nt.nteAddress.aSocket = 0;
-
- np = nte.nt.entityData;
- *np++ = '\1';
- *np++ = '='; /*** all entities of this type please ***/
- BlockMove( theType, np, (long)(*theType+1));
- np += (*theType)+1;
- BlockMove( theZone, np, (long)(*theZone+1) );
-
- Mpb.NBPntQElPtr = nte.nt.entityData;
- Mpb.NBPretBuffPtr = *(nbp->LookUpBuffer);
- Mpb.NBPretBuffSize = (short)(EntSize * num);
- Mpb.NBPmaxToGet = num;
- Mpb.NBPinterval = interval;
- Mpb.NBPcount = count;
-
- if( !(error = PLookupName( &Mpb, SYNC)) )
- nbp->EntCount = Mpb.NBPnumGotten;
-
- HUnlock( nbp->LookUpBuffer );
- return( nbp->EntCount );
- }
-
-
- short NodeRegister( nbp, nameOf, typeOf, count, interval, verify, addr )
- NBPBlock *nbp;
- char *typeOf;
- char *nameOf;
- short count;
- short interval;
- short verify;
- AddrBlock *addr;
- /*********************************
- * Register this node as the entity
- * passed as a parameter.
- *
- * In: (Ptr) typeof entity (provided by hyperTalk)
- * (Ptr) nameof entity (from system resource)
- * short retry interval
- * short retry count
- * short verify = name must be unique.
- * Out: noErr if successful
- * OSErr otherwise
- *********************************/
- {
- /* auto */ short error;
- register NamesTableEntry *nte = &(nbp->NTEntry);
- register char *np; /*** pointer into names table ***/
- /* auto */ MPPParamBlock Mpb;
-
- /*** save off the entity in a packed names array ***/
- np = nte->nt.entityData;
- BlockMove( nameOf, np, (long)(*nameOf)+1 );
- np += (*nameOf)+1;
- BlockMove( typeOf, np, (long)(*typeOf)+1 );
- np += (*typeOf)+1;
- *np++ = '\1';
- *np++ = '*';
-
- nte->nt.nteAddress.aSocket = addr->aSocket;
- nte->nt.nteAddress.aNode = addr->aNode;
- nte->nt.nteAddress.aNet = addr->aNet;
-
- Mpb.NBPinterval = interval;
- Mpb.NBPcount = count;
- Mpb.NBPntQElPtr = nte;
- Mpb.NBPverifyFlag = verify;
-
- error = PRegisterName( &Mpb, SYNC);
-
- if( !error )
- nbp->Registered = true;
-
- return( error );
- }
-
-